/*
 * JSFConsole - Lightweight JSF Application Monitor
 * Copyright (C) 2009  Grzegorz Bugaj
 * http://www.gregbugaj.com
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/
package com.gregbugaj.reflector.visitors;	

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.w3c.dom.Node;

import com.gregbugaj.jsfconsole.util.ReflectUtil;
import com.gregbugaj.jsfconsole.util.reflection.ReflectionProviderStrategy;
import com.gregbugaj.reflector.producers.ArrayNodeProducer;
import com.gregbugaj.reflector.producers.CollectionNodeProducer;
import com.gregbugaj.reflector.producers.ComplexNodeProducer;
import com.gregbugaj.reflector.producers.MapNodeProducer;
import com.gregbugaj.reflector.producers.MethodNodeProducer;
import com.gregbugaj.reflector.producers.ReferenceNodeProducer;
import com.gregbugaj.reflector.producers.SimpleNodeProducer;

public class XMLVisitor extends  AbstractVisitor {
	public XMLVisitor(){
		super();
	}	


	public void visitCollection(final Collection<?> o, final Field f) {		
		CollectionNodeProducer node=(CollectionNodeProducer) new CollectionNodeProducer(context).set(o, f);
		context.add(node, true);
		for(Object item : o){
			visit(item, f);
		}
	}

	public void visitMap(final Map<?, ?> o, final Field f) {
		MapNodeProducer node=(MapNodeProducer) new MapNodeProducer(context).set(o, f);
		context.add(node, true);
		try{
			for(Object item:o.entrySet()){		
				Object key=((Entry<?,?>)item).getKey();
				Object value=((Entry<?,?>)item).getValue();
				node.startEntryNode();

				visit(key, f);
				visit(value, f);

				node.endEntryNode();
			}
		}catch(java.lang.UnsupportedOperationException uoe){
			//Suppressed since not everybody provides entry set operation
		}catch(Exception e){
			e.printStackTrace();
		}
	}


	public void visitArray(final ArrayWrapper wrapper, final Field f) {
		Node parent=context.getParent();

		ArrayNodeProducer node=(ArrayNodeProducer) new ArrayNodeProducer(context).set(wrapper, f);
		context.add(node, true);
		for(int i=0;i<wrapper.getLength();i++){
			Object item=wrapper.next();
			visit(item, f);
		}
		//Restore parent
		context.setParent(parent);
	}


	public void visitMethod(final Object o, final Method m) {
		Node parent=context.getParent();
		MethodNodeProducer node=new MethodNodeProducer(context).set(o, m);
		context.add(node, true);
		//c("<method signature=\""+m+"\">", depth);
		try{
			visit(ReflectUtil.getMethodValue(o, m));
		}catch(Exception ex){
			ex.printStackTrace();
		}

		//Restore parent
		context.setParent(parent);
	}


	public void visitSimple(final Object o, final Field f) {
		context.add(new SimpleNodeProducer(context).set(o, f));
	}


	public void visitComplex(final Object obj, final Field f) {
		ReflectionProviderStrategy reflectionProvider = context.getReflectionProviderStrategy();

		//c((null!=f)?f.getName():""+"<object name=\""+ o.getClass().getSimpleName()+"\" class=\""+ o.getClass().getCanonicalName()+"\"> ",depth);
		ComplexNodeProducer node=(ComplexNodeProducer) new ComplexNodeProducer(context).set(obj, f);
		context.add(node, true);

		try {						
			List<Field> fields=reflectionProvider.getAccesibleFields(obj);
			node.startFieldsNode();
			for(Field field:fields){
				Object out=field.get(obj);
				visit(out, field);
			}
			node.endFieldsNode();
			node.startMethodsNode();
			/*
			for(Method method : reflectionProvider.getMethods(objClazz)){	
				visit(obj, method);
			}
			 */
			node.endMethodsNode();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		} 
	}


	public void visitReference(final Object o, final Field f) {
		context.add(new ReferenceNodeProducer(context).set(o, f));
	}


	public void visitReference(final Object o, final Method m) {
		context.add(new ReferenceNodeProducer(context).set(o, m));
	}


	public void visitEnum(final Enum<?> o, final Field f) {
		context.add(new SimpleNodeProducer(context).set(o, f));
	}

}
